package com.g.ocache.utils;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author Jingqi Xu
 */
public final class XThreadFactory implements ThreadFactory {
	//
	private static final Logger LOGGER = LoggerFactory.getLogger(XThreadFactory.class);
	
	//
	private String name;
	private boolean daemon;
	private UncaughtExceptionHandler uncaughtExceptionHandler;
	private final ConcurrentHashMap<String, AtomicLong> sequences;
	
	
	/**
	 * 
	 */
	public XThreadFactory() {
		this(null, false, null);
	}
	
	public XThreadFactory(String name) {
		this(name, false, null);
	}
	
	public XThreadFactory(String name, boolean daemon) {
		this(name, daemon, null);
	}
	
	public XThreadFactory(String name, boolean daemon, UncaughtExceptionHandler handler) {
		this.name = name;
		this.daemon = daemon;
		this.uncaughtExceptionHandler = handler;
		this.sequences = new ConcurrentHashMap<String, AtomicLong>();
	}

	/**
	 * 
	 */
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public boolean isDaemon() {
		return daemon;
	}
	
	public void setDaemon(boolean daemon) {
		this.daemon = daemon;
	}

	public UncaughtExceptionHandler getUncaughtExceptionHandler() {
		return uncaughtExceptionHandler;
	}
	
	public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
		this.uncaughtExceptionHandler = handler;
	}
	
	/**
	 * 
	 */
	public Thread newThread(Runnable r) {
		//
		Thread t = new Thread(r);
		t.setDaemon(this.daemon);
		
		//
		String prefix = this.name;
		if(prefix == null || prefix.equals("")) {
			prefix = getInvoker(2);
		}
		t.setName(prefix + "-" + getSequence(prefix));
		
		//
		if(this.uncaughtExceptionHandler != null) {
			t.setUncaughtExceptionHandler(this.uncaughtExceptionHandler);
		} else {
			t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
				public void uncaughtException(Thread t, Throwable e) {
					LOGGER.error("unhandled exception in thread: " + t.getId() + ":" + t.getName(), e);
				}
			});
		}
		
		//
		return t;
	}

	/**
	 * 
	 */
	private String getInvoker(int depth) {
		Exception e = new Exception();
		StackTraceElement[] stes = e.getStackTrace();
		if(stes.length > depth) {
			return ClassUtils.getShortClassName(stes[depth].getClassName());
		}
		return getClass().getSimpleName();
	}
	
	private long getSequence(String invoker) {
		AtomicLong r = this.sequences.get(invoker);
		if(r == null) {
			r = new AtomicLong(0);
			AtomicLong previous = this.sequences.putIfAbsent(invoker, r);
			if(previous != null) {
				r = previous;
			}
		}
		return r.incrementAndGet();
	}
}
